/**
 * 简答题
 *
 * 一、谈谈你是如何理解JS异步编程的, Eventloop、消息队列都是做什么的, 什么是宏任务,什么是微任务?
 */
// ------------------------------------------------------------
/* 
  使部分耗时任务可以暂时脱离主任务栈以外继续执行，主代码继续向下执行，而不会阻塞其他主线程代码的执行，等异步任务完成时再加入到任务栈中，顺序执行
  Eventloop 就是在消息队列和任务栈中起到监管调度的指挥官，当任务栈中的任务都执行完毕以后，从消息队列中依次调用消息队列中的任务，这整个过程就是一个Eventloop
  消息队列是有别于主任务栈以外的另一个任务队列，用于排列等待进入主任务栈执行的异步任务
  宏任务包括 主代码 和 定时器任务等
  微任务是在下一个宏任务之前插入执行的一些任务 例如 Promise 和 回调函数 等
 */

/**
 * 代码题
 * 一、将下面异步代码使用 Promise 的方式改进
 */
setTimeout(function () {
  var a = 'hello';
  setTimeout(function () {
    var b = 'lagou';
    setTimeout(function () {
      var c = 'I ❤ U';
      console.log(a + b + c);
    }, 10);
  }, 10);
}, 10);
// ------------------------------------------------------------
const timeoutPromise = (callback, delay) =>
  new Promise((resolve) =>
    setTimeout(() => {
      callback && callback();
      resolve();
    }, delay)
  );
let a, b, c;

timeoutPromise(() => (a = 'hello'), 10)
  .then(() => timeoutPromise(() => (b = 'lagou'), 10))
  .then(() =>
    timeoutPromise(() => {
      c = 'I ❤ U';
      console.log(a + b + c);
    }, 10)
  );

const { flowRight } = require('lodash/fp');
/**
 * 二、基于以下代码完成下面的四个练习
 */
const fp = require('lodash/fp');
// 数据
// horsepower 马力, dollar_value 价格, in_stock 库存
const cars = [
  {
    name: 'Ferrari FF',
    horsepower: 660,
    dollar_value: 700000,
    in_stock: true,
  },
  {
    name: 'Spyker C12 Zagato',
    horsepower: 650,
    dollar_value: 648000,
    in_stock: false,
  },
  {
    name: 'Jaguar XKR-S',
    horsepower: 550,
    dollar_value: 132000,
    in_stock: false,
  },
  {
    name: 'Audi R8',
    horsepower: 525,
    dollar_value: 114200,
    in_stock: false,
  },
  {
    name: 'Aston Martin One-77',
    horsepower: 750,
    dollar_value: 1850000,
    in_stock: true,
  },
  {
    name: 'Pagani Huayra',
    horsepower: 700,
    dollar_value: 1300000,
    in_stock: false,
  },
];

/*
  练习1: 使用函数组合 fp.flowRight() 重新实现下面这个函数
*/
let isLastInStock = function (cars) {
  //获取最后一条数据
  let last_car = fp.last(cars);
  //获取最后一条数据的 in_stock 属性值
  return fp.prop('in_stock', last_car);
};
// ------------------------------------------------------------
const myIsLastInStock = fp.flowRight(fp.prop('in_stock'), fp.last);
console.log('\nisLastInStock', isLastInStock(cars));
console.log('myIsLastInStock', myIsLastInStock(cars));

/*
  练习2: 使用fp.flowRight、fp.prop() 和 fp.first() 获取第一个 car 的 name
*/
// ------------------------------------------------------------
const getFirstCarName = fp.flowRight(fp.prop('name'), fp.first);
console.log('\ngetFirstCarName', getFirstCarName(cars));

/*
  练习3: 使用帮助函数 _average 重构 averageDollarValue, 使用函数组合的方式实现
*/
let _average = function (xs) {
  return fp.reduce(fp.add, 0, xs) / xs.length;
}; //<-无须改动
let averageDollarValue = function (cars) {
  let dollar_values = fp.map(function (car) {
    return car.dollar_value;
  }, cars);
  return _average(dollar_values);
};
// ------------------------------------------------------------
console.log('\naverageDollarValue', averageDollarValue(cars));
const myAverageDollarValue = fp.flowRight(
  _average,
  fp.map((car) => car.dollar_value)
);
console.log('myAverageDollarValue', myAverageDollarValue(cars));

/*
  练习4: 使用 flowRight 写一个 sanitizeNames() 函数,返回一个下划线连接的小写字符串,把数组中的 name 转换为这种形式:例如
  sanitizeNames(["Hello World"]) => ["hello_world"]
*/
let _underscore = fp.replace(/\W+/g, '_'); //<--无须改动,并在 sanitizeNames 中使用它
// ------------------------------------------------------------
const sanitizeNames = fp.flowRight(
  fp.map((e) => fp.flowRight(_underscore, fp.toLower)(e))
);
console.log('\nsanitizeNames', sanitizeNames(['Hello World']));

/*
  三、基于下面提供的代码,完成后续的四个练习
*/
class Container {
  static of(value) {
    return new Container(value);
  }
  constructor(value) {
    this._value = value;
  }
  map(fn) {
    return Container.of(fn(this._value));
  }
}

class Maybe {
  static of(value) {
    return new Maybe(value);
  }

  constructor(value) {
    this._value = value;
  }

  isNothing() {
    return this._value === null || this._value === undefined;
  }

  map(fn) {
    return this.isNothing() ? this : Maybe.of(fn(this._value));
  }
}

module.exports = { Maybe, Container };

/*
  练习1: 使用 fp.add(x,y) 和 fp.map(f,x) 创建一个能让 functor 里的值增加的函数 ex1
*/
let maybe = Maybe.of([5, 6, 1]);
let ex1 = () => {
  //你需要实现的函数。。。
  // ------------------------------------------------------------
  return maybe.map((value) => fp.map((e) => fp.add(e, 1))(value))._value;
};
console.log('\nex1', ex1());

/*
  练习2: 实现一个函数 ex2, 能够使用 fp.first 获取列表的第一个元素
*/
let xs = Container.of(['do', 'ray', 'me', 'fa', 'so', 'la', 'ti', 'do']);
let ex2 = () => {
  //你需要实现的函数。。。
  // ------------------------------------------------------------
  return xs.map(fp.first)._value;
};
console.log('\nex2', ex2());

/*
  练习3: 实现一个函数 ex3, 使用 safeProp 和 fp.first 找到 user 的名字的首字母
*/
let safeProp = fp.curry(function (x, o) {
  return Maybe.of(o[x]);
});
let user = { id: 2, name: 'Albert' };
let ex3 = () => {
  //你需要实现的函数。。。
  // ------------------------------------------------------------
  return safeProp('name')(user).map((value) => fp.first(value))._value;
};
console.log('\nex3', ex3());

/*
  练习4: 使用 Maybe 重写 ex4, 不要有if语句
*/
let ex4 = function (n) {
  if (n) {
    return parseInt(n);
  }
};
// ------------------------------------------------------------
const myEx4 = (n) => Maybe.of(n).map(parseInt)._value;

console.log('\nex4', ex4(1.2), ex4(null), ex4(undefined));
console.log('myEx4', myEx4(1.2), myEx4(null), myEx4(undefined));

/**
 * 四、手写实现 Mypromise 源码
 * 要求: 尽可能还原 Promise 中的每一个API,并通过注释的方式描述思路和原理。
 */
// ------------------------------------------------------------
const MyPromise = require('../components/Promise');
MyPromise.resolve(123).then((res) =>
  console.log('\nthis is MyPromise', res, '\n')
);
